package com.xiaolong.web.handler;

import cn.hutool.core.util.StrUtil;
import com.xiaolong.common.context.ModelViewResponse;
import com.xiaolong.common.exception.BusinessException;
import io.swagger.v3.oas.models.Paths;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

@RestControllerAdvice(
    annotations = {RestController.class, RestControllerAdvice.class}
)
public class ApiResponseBodyHandler implements ResponseBodyAdvice<Object> {
    private static final Logger log = LoggerFactory.getLogger(ApiResponseBodyHandler.class);
    Method rootContextMethod = null;

    public ApiResponseBodyHandler() {
        try {
            //通过io.seata.core.context.RootContext#getXID
            // 来自SeataFeignRequestInterceptor
            Class<?> rootContextClass = Class.forName("io.seata.core.context.RootContext");
            this.rootContextMethod = rootContextClass.getMethod("getXID");
        } catch (NoSuchMethodException | ClassNotFoundException var3) {
        }

    }

    @Override
    public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object object, MethodParameter returnType, MediaType mediaType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        String path = request.getURI().getPath();
        if (path.contains("/v3/api-docs") || path.contains("/swagger-ui")) {
            return object;
        }
        if (object instanceof ModelViewResponse) {
            return object;
        } else if (object instanceof byte[]) {
            return object;
        } else if (returnType.getDeclaringClass().getName().equals(Paths.class.getName())) {
            return object;
        } else {
            return MediaType.TEXT_HTML.equals(mediaType) ? object : ModelViewResponse.success(object);
        }
    }

    @ExceptionHandler({MethodArgumentNotValidException.class})
    public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletResponse response) {
        this.setRespErrStatus(response);
        String message = e.getBindingResult().getFieldError().getDefaultMessage();
        return ModelViewResponse.error( message);
    }


    @ExceptionHandler({Throwable.class})
    public Object exceptionResponse(HttpServletRequest request, HttpServletResponse response, Throwable throwable) {
        this.setRespErrStatus(response);
        this.printErrorMessage(request, throwable);
        return ModelViewResponse.error("系统错误");
    }

    @ExceptionHandler({BusinessException.class})
    public Object exceptionResponse(HttpServletRequest request, HttpServletResponse response, BusinessException e) {

        this.setRespErrStatus(response);
        this.printErrorMessage(request, e);
        return ModelViewResponse.error(e.getCode(), e.getMessage());
    }

    private void setRespErrStatus(HttpServletResponse response) {
        //String txXid = request.getHeader("TX_XID"); 都可以
        if (this.rootContextMethod != null) {
            try {
                String xid = (String)this.rootContextMethod.invoke((Object)null);
                if (StrUtil.isNotBlank(xid)) {
                    response.setStatus(500);
                }
            } catch (InvocationTargetException | IllegalAccessException var3) {
            }

        }
    }

    public void printErrorMessage(HttpServletRequest request, Throwable throwable) {
        String errorMessage = throwable.getMessage();
        String format = "发生异常：%s，异常信息：%s，请求地址：%s，请求方式：%s";
        String errorMsg = String.format(format, throwable.getClass().getSimpleName(), errorMessage, request.getRequestURI(), request.getMethod());
        log.error(errorMsg, throwable);
    }


}